package cn.lg.soar.system.auth.client;

import cn.lg.soar.common.util.current.ICurrentUser;
import cn.lg.soar.common.util.current.UserContext;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.core.annotations.ApiClient;
import cn.lg.soar.database.annotations.DataScopeIgnore;
import cn.lg.soar.database.datascope.Filter;
import cn.lg.soar.database.datascope.IDataPermissionService;
import cn.lg.soar.database.enums.ProvideTypeEnum;
import cn.lg.soar.system.api.constant.ModuleConst;
import cn.lg.soar.system.auth.entity.Resource;
import cn.lg.soar.system.auth.model.DataRuleItemDTO;
import cn.lg.soar.system.auth.service.IDataRuleProvideService;
import cn.lg.soar.system.auth.service.IDataScopeService;
import cn.lg.soar.system.auth.service.IResourceService;
import cn.lg.soar.system.auth.service.IUserService;
import cn.lg.soar.system.config.AuthCacheKey;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Primary;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author luguoxiang
 * @date 2022/1/3
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@ConditionalOnProperty(name = "lg.database.enabled-data-permission", havingValue = "true", matchIfMissing = true)
@Primary
@ApiIgnore
@ApiClient("boot")
@RestController
@RequestMapping(ModuleConst.CLIENT_PATH + "/datapermission")
public class DataPermissionApiClient implements IDataPermissionService {

    @Autowired
    private IUserService userService;
    @Autowired
    private IResourceService resourceService;
    @Autowired
    private IDataScopeService dataScopeService;
    @Autowired
    private IDataRuleProvideService dataRuleProvideService;

    @DataScopeIgnore
    @Cacheable(value = AuthCacheKey.RESOURCE_ID, key = "#resource", cacheManager = "remote")
    @GetMapping("mapId")
    @Override
    public Long mapId(String resource) {
        Resource data = resourceService.getOne(
                Wrappers.<Resource>lambdaQuery()
                        .select(Resource::getId)
                        .eq(Resource::getDisabled, false)
                        .eq(Resource::getCode, resource)
        );
        if (data == null) {
            return -1L;
        }
        return data.getId();
    }

    @DataScopeIgnore
    @GetMapping("dataScopes")
    @Override
    public List<List<Filter>> dataScopes(Long resourceId) {
        if (resourceId == null) {
            return null;
        }
        Map<Long, List<DataRuleItemDTO>> resourceRuleMap = dataScopeService.getResourceRuleMap(resourceId);
        if (resourceRuleMap == null) {
            return null;
        }
        ICurrentUser user = UserContext.getUser();
        if (userService.isSuperAdministrator(user.getUsername())) {
            // 超级管理员拥有全部数据权限
            return Collections.singletonList(Collections.emptyList());
        }
        List<Long> roleIds = userService.getUserCurrentAllRoleIds(user.getId());
        Set<List<DataRuleItemDTO>> set;
        if (DataUtil.isValuable(roleIds)) {
            set = roleIds.stream()
                    .map(resourceRuleMap::get)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
        } else {
            set = null;
        }
        if (DataUtil.isEmpty(set)) {
            // 取缺省规则
            List<DataRuleItemDTO> items = resourceRuleMap.get(0L);
            if (items == null) {
                return Collections.emptyList();
            }
            set = Collections.singleton(items);
        }

        return set.stream()
                .map(ls -> ls.stream()
                        .map(x -> {
                            String columnValue = x.getColumnValue();
                            ProvideTypeEnum provideType = x.getProvideType();
                            Object value = dataRuleProvideService.getColumnValue(provideType, columnValue);
                            Filter filter = new Filter();
                            filter.setTableName(x.getTableName());
                            filter.setColumnName(x.getColumnName());
                            filter.setExpression(x.getExpression());
                            filter.setColumnValue(value);
                            filter.setSpliceType(x.getSpliceType());
                            return filter;
                        })
                        .collect(Collectors.toList())
                ).collect(Collectors.toList());
    }

}
